#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
作  者    : 北极星光 light22@126.com
创建时间  : 2023-07-11 18:13:58
最后编辑  : 2023-09-03 15:05:55
编辑人    : 北极星光
'''

# 文档地址： https://github.com/tencent-connect/botpy

import re
import time
import botpy
import logging
from apps.DeepseekAI import DeepseekAI
from apps.ernieAI import ErnieAI
from apps.tools import *
from botpy.message import Message, GroupMessage
from botpy.message import Message, GroupMessage
from botpy.ext.cog_yaml import read
from logging import getLogger, FileHandler

bot_config = read(r"./config/qqbot_config.yaml")
ds_config = read(r"./config/ds_config.yaml")
ernie_config = read(r"./config/ernie_config.yaml")

# 获取日志级别配置
log_level = bot_config.get('log_level')
log_file_level = bot_config.get('log_file_level')

# 将字符串级别转换为 logging 常量
log_level = getattr(logging, log_level.upper(), logging.INFO)
log_file_level = getattr(logging, log_file_level.upper(), logging.INFO)

# 创建专用日志记录器
_log = getLogger('qqbot')
_log.setLevel(log_level)

if not _log.handlers:
    # 详细日志格式
    detailed_format = '%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(funcName)s - %(message)s'

    # 创建并配置专属文件处理器
    qq_file_handler = FileHandler(
        filename=f'logs/qqbot_{time.strftime("%Y-%m-%d")}.log',
        mode='a',
        encoding='utf-8')
    qq_file_handler.setLevel(log_file_level)
    qq_file_handler.setFormatter(logging.Formatter(detailed_format))

    # 创建控制台处理器
    qq_console_handler = logging.StreamHandler()
    qq_console_handler.setLevel(log_level)
    qq_console_handler.setFormatter(logging.Formatter(detailed_format))

    # 只添加到专用记录器
    _log.addHandler(qq_file_handler)
    _log.addHandler(qq_console_handler)

# 实例化DeepseekAI
deepseek_ai = DeepseekAI(api_key=ds_config.get('api_key'),
                         model_name=ds_config.get('model_name'),
                         bot_name=ds_config.get('bot_name'),
                         bot_role=ds_config.get('bot_role'),
                         welcome_words=ds_config.get('welcome_words'),
                         sensitive_words=ds_config.get('sensitive_words'))
# 实例化ErnieAI
ernie_ai = ErnieAI(api_key=ernie_config.get('api_key'),
                   bot_name=ernie_config.get('bot_name'),
                   bot_role=ernie_config.get('bot_role'),
                   welcome_words=ernie_config.get('welcome_words'),
                   sensitive_words=ernie_config.get('sensitive_words'))


class MyClient(botpy.Client):

    async def on_ready(self):
        _log.info(
            f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]准备就绪!")

    # 频道消息事件
    async def on_at_message_create(self, message: Message):
        name = message.author.username
        user_id = message.author.id
        user = f'{name}({user_id})'
        _log.info(
            f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]收到消息：{user}：{message.content}"
        )

        # DeepseekAI对话
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?([dD][sS]对话.*)$",
                    message.content):
            match = re.match(
                f"^(<@!{message.mentions[0].id}>\s+)?[/#]?([dD][sS]对话.*)$",
                message.content)
            # 构造消息发送请求数据对象
            if match.group(2):
                message_content = match.group(2).split('对话')[1].strip()
                reply_content = deepseek_ai.deepseek_chat(message_content,
                                                          user=user)
                html_path = md_to_html(reply_content, 'output/output.html',welcome_words=ds_config.get('welcome_words'))
                _log.info(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
                )
                img_path = html_to_img('output/output.html',
                                       'output/output.jpg')
                _log.info(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
                )
            else:
                reply_content = deepseek_ai.welcome_words
                html_path = md_to_html(reply_content, 'output/output.html',welcome_words=ds_config.get('welcome_words'))
                _log.info(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
                )
                img_path = html_to_img('output/output.html',
                                       'output/output.jpg')
                _log.info(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
                )

            # 通过api发送回复消息
            await self.api.post_message(channel_id=message.channel_id,
                                        content=f'回复{name}：',
                                        msg_id=message.id,
                                        file_image='output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【DeepSeekAI】成功回复 {name}"
            )

        # 列出所有模型
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?([dD][sS]模型.*)$",
                    message.content):
            # 构造消息发送请求数据对象
            reply_content = deepseek_ai.list_models()
            html_path = md_to_html(reply_content, 'output/output.html',welcome_words=ds_config.get('welcome_words'))
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
            )
            img_path = html_to_img('output/output.html', 'output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
            )

            # 通过api发送回复消息
            await self.api.post_message(channel_id=message.channel_id,
                                        content=f'回复{name}：',
                                        msg_id=message.id,
                                        file_image='output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【DeepSeekAI】成功回复 {name}"
            )

        # 查询余额
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?([dD][sS]余额.*)$",
                    message.content):
            # 构造消息发送请求数据对象
            reply_content = deepseek_ai.get_balance()
            html_path = md_to_html(reply_content, 'output/output.html',welcome_words=ds_config.get('welcome_words'))
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
            )
            img_path = html_to_img('output/output.html', 'output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
            )

            # 通过api发送回复消息
            await self.api.post_message(channel_id=message.channel_id,
                                        content=f'回复{name}：',
                                        msg_id=message.id,
                                        file_image='output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【DeepSeekAI】成功回复 {name}"
            )

        # 重置会话
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?([dD][sS]重置.*)$",
                    message.content):
            deepseek_ai.reset_messages(user=user)
            await self.api.post_message(channel_id=message.channel_id,
                                        content=f'回复{name}：会话已重置',
                                        msg_id=message.id)
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【DeepSeekAI】成功重置消息 {name}"
            )

        # 帮助信息
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?([dD][sS]帮助.*)$",
                    message.content):
            # 构造消息发送请求数据对象
            reply_content = f'''### DeepSeekAI帮助
- /DS对话：AI对话（指令后跟对话内容）
- /DS模型：列出所有模型
- /DS余额：查询余额
- /DS重置：重置会话
- /DS帮助：显示帮助信息'''
            html_path = md_to_html(reply_content, 'output/output.html',welcome_words=ds_config.get('welcome_words'))
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
            )
            img_path = html_to_img('output/output.html', 'output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
            )

            # 通过api发送回复消息
            await self.api.post_message(channel_id=message.channel_id,
                                        content=f'回复{name}：',
                                        msg_id=message.id,
                                        file_image='output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【DeepSeekAI】成功回复 {name}"
            )

        # 文心一言
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?(文心一言.*)$",
                    message.content):
            match = re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?(文心一言.*)$",
                            message.content)

            # 构造消息发送请求数据对象
            if match.group(2):
                message_content = match.group(2).split('一言')[1].strip()
                reply_content = ernie_ai.ernie_text_generate(message_content, model_name=ernie_config.get('model_name_text_generate'), user=user)
                html_path = md_to_html(reply_content, 'output/output.html',welcome_words=ernie_config.get('welcome_words'))
                _log.info(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
                )
                img_path = html_to_img('output/output.html',
                                       'output/output.jpg')
                _log.info(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
                )
            else:
                reply_content = ernie_ai.welcome_words
                html_path = md_to_html(reply_content, 'output/output.html',welcome_words=ernie_config.get('welcome_words'))
                _log.info(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
                )
                img_path = html_to_img('output/output.html',
                                       'output/output.jpg')
                _log.info(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
                )

            # 通过api发送回复消息
            await self.api.post_message(channel_id=message.channel_id,
                                        content=f'回复{name}：',
                                        msg_id=message.id,
                                        file_image='output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【文心一言】成功回复 {name}"
            )

        # 文心画图
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?(文心[绘画]图.*)$",
                    message.content):
            match = re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?(文心[绘画]图.*)$",
                            message.content)

            # 构造消息发送请求数据对象
            message_content = match.group(2).split('图')[1].strip()
            
            response = ernie_ai.ernie_image_generate(message_content, model_name=ernie_config.get('model_name_image_generate'))
            if response.get('error'):
                await self.api.post_message(channel_id=message.channel_id,
                                            content=f"回复{name}：生成图片失败:{response.get('error').get('message')}",
                                            msg_id=message.id)
                _log.error(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【文心一言】生成图片失败 {response.get('error').get('message')}"
                )
            else:
                img_url = response.get('data')[0].get('url')
                # 通过api发送回复消息
                await self.api.post_message(channel_id=message.channel_id,
                                            content=f'回复{name}：',
                                            msg_id=message.id,
                                            image=img_url)
                _log.info(
                    f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【文心一言】成功回复 {name}"
                )
        
        # 列出所有模型
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?(文心模型.*)$",
                    message.content):
            # 构造消息发送请求数据对象
            reply_content = ernie_ai.list_models()
            html_path = md_to_html(reply_content, 'output/output.html',welcome_words=ernie_config.get('welcome_words'))
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
            )
            img_path = html_to_img('output/output.html', 'output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
            )

            # 通过api发送回复消息
            await self.api.post_message(channel_id=message.channel_id,
                                        content=f'回复{name}：',
                                        msg_id=message.id,
                                        file_image='output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【文心一言】成功回复 {name}"
            )

        # 重置会话
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?(文心重置.*)$",
                    message.content):
            ernie_ai.reset_messages(user=user)
            await self.api.post_message(channel_id=message.channel_id,
                                        content=f'回复{name}：会话已重置',
                                        msg_id=message.id)
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【文心一言】成功重置消息 {name}"
            )

        # 帮助信息
        if re.match(f"^(<@!{message.mentions[0].id}>\s+)?[/#]?(文心帮助.*)$",
                    message.content):
            # 构造消息发送请求数据对象
            reply_content = f'''### 文心一言帮助
- /文心一言：AI生成文本（指令后跟对话内容）
- /文心画图、/文心绘图：AI生成图片（指令后跟图片描述）
- /文心模型：列出所有模型
- /文心重置：重置会话
- /文心帮助：显示帮助信息'''
            html_path = md_to_html(reply_content, 'output/output.html',welcome_words=ernie_config.get('welcome_words'))
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
            )
            img_path = html_to_img('output/output.html', 'output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
            )

            # 通过api发送回复消息
            await self.api.post_message(channel_id=message.channel_id,
                                        content=f'回复{name}：',
                                        msg_id=message.id,
                                        file_image='output/output.jpg')
            _log.info(
                f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【文心一言】成功回复 {name}"
            )


        # 调试用
        # _log.info(deepseek_ai.messages)
        # _log.info(ernie_ai.messages)

    # 群消息事件（暂时咕咕）
    # async def on_group_at_message_create(self, message: GroupMessage):
    #     # _log.info(f'message.content: {message.content}')  #  /DS对话 
    #     # _log.info(f'message.attachments: {message.attachments}')  #  []
    #     # _log.info(f'message.author: {message.author}')  # {'member_openid': '08AB2A8B924A647FFBB28B3A8773DFFC'}
    #     # _log.info(f'message.event_id: {message.event_id}')  # GROUP_AT_MESSAGE_CREATE:ywuzmzicfembw3sjjk9hm0fuqaawu2kbrfx6cnumdxxqkgxwpynhysl6q6ws849
    #     # _log.info(f'message.id: {message.id}')  # ROBOT1.0_YWUzMZI.cfeMBw3SjJk9Hh5KZ5iWitkYRZ5uR.BjX491zNNpOFo2sDAVOsb0ovnDu1gfnz-PtvTia1vbFELY5Ro1S3SST4qkbcnDrcr9.7s!
    #     # _log.info(f'message.group_openid: {message.group_openid}')  # 4F6C8354257F52403B08D5F8B0E78931
    #     # _log.info(f'message.mentions: {message.mentions}')  # []
    #     # _log.info(f'message.message_reference: {message.message_reference}')  # {'message_id': None}
    #     # _log.info(f'message.msg_seq: {message.msg_seq}')  # None
        
    #     user = message.author.member_openid
    #     _log.info(
    #         f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]收到消息：{user} ：{message.content}"
    #     )

    #     # DeepseekAI对话
    #     if re.match(f"^(\s+)?[/#]?([dD][sS]对话.*)$",
    #                 message.content):
    #         match = re.match(
    #             f"^(\s+)?[/#]?([dD][sS]对话.*)$",
    #             message.content)
        
    #     # 构造消息发送请求数据对象
    #         if match.group(2):
    #             message_content = match.group(2).split('对话')[1].strip()
    #             reply_content = deepseek_ai.deepseek_chat(message_content,
    #                                                       user=user)
    #             html_path = md_to_html(reply_content, 'output/output.html')
    #             _log.info(
    #                 f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
    #             )
    #             img_path = html_to_img('output/output.html',
    #                                    'output/output.jpg')
    #             _log.info(
    #                 f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
    #             )
    #         else:
    #             reply_content = deepseek_ai.welcome_words
    #             html_path = md_to_html(reply_content, 'output/output.html')
    #             _log.info(
    #                 f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成HTML文件{html_path}"
    #             )
    #             img_path = html_to_img('output/output.html',
    #                                    'output/output.jpg')
    #             _log.info(
    #                 f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]生成图片文件{img_path}"
    #             )

    #         # 通过api发送回复消息
    #         file_url = 'https://127.0.0.1:5000/output.jpg'  # 这里需要填写上传的资源Url
    #         _log.info(file_url)
    #         uploadMedia = await message._api.post_group_file(
    #             group_openid=message.group_openid,
    #             file_type=1,  # 文件类型要对应上，具体支持的类型见方法说明
    #             url=file_url  # 文件Url
    #         )
            
    #         await message._api.post_group_message(
    #             group_openid=message.group_openid,
    #             msg_type=7,  # 7表示富媒体类型
    #             msg_id=message.id,
    #             media=uploadMedia)
    #         _log.info(
    #             f"「{self.robot.name}」 [{time.strftime('%Y-%m-%d %H:%M:%S')}]【DeepSeekAI】成功回复 {user}"
    #         )


    #     # 调试用
    #     # _log.info(deepseek_ai.messages)
    #     # _log.info(ernie_ai.messages)

def qqbot_run():
    if bot_config.get('enable_qqbot') == True:
        intents = botpy.Intents(public_guild_messages=True, public_messages=True)
        client = MyClient(intents=intents, bot_log=None)
        client.run(appid=bot_config.get('appid'), secret=bot_config.get('secret'))


if __name__ == '__main__':
    qqbot_run()
